package com.sunset.auth;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

/**
 * FileName: WebSecurityConfig
 * Author:   Sunset
 * Date:     2021/3/13 15:14
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;

    @Autowired
    private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
    /**
     * 匿名用户访问无权限资源时的异常
     */
    @Autowired
    private MyAuthenticationEntryPoint myAuthenticationEntryPoint;

    @Autowired
    private MyAccessDeniedHandler myAccessDeniedHandler;

    @Autowired
    private MyLogoutHandler myLogoutHandler;

    @Autowired
    private MyLogoutSuccessHandler myLogoutSuccessHandler;
    /**
     * 超时管理
     */
    @Autowired
    private MyInvalidSessionStrategy myInvalidSessionStrategy;
    /**
     * 登录被挤下线的处理方式
     */
    @Autowired
    private MyExpiredSessionStrategy myExpiredSessionStrategy;

    /**
     * 通过auth可以在内存中构建虚拟的用户名和密码
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //super.configure(auth);
        /*auth.inMemoryAuthentication()
                .withUser("user")
                .password(passwordEncoder.encode("123"))
                .roles("user")
                .and()
                .withUser("admin")
                .password(passwordEncoder.encode("123"))
                .roles("admin")
                .and()
                //配置auth的加密方式为passwordEncoder
                //如果上述.password为明文加密，则下面的passwordEncoder不需要出现
                .passwordEncoder(passwordEncoder);*/
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //开启httpBasic认证
        /*http.httpBasic()
                .and()//算是一种链式编程，表示连接符，返回http
                //认证所有的请求
                .authorizeRequests()
                //任何请求都必须认证成功
                .anyRequest()
                .authenticated();*/
        //任何请求都会被认证，所有的请求都会被拦截，包括登录 html
        //访问home被拦截了 跳转到login login也会被拦截 导致页面一直不能正确的重定向
        http.authorizeRequests()
                //放行不需要拦截的请求
                .antMatchers("/login", "/login.html").permitAll()
                //这里是配置访问什么资源需要什么样的权限/角色  访问users和roles的话有user和admin，要是访问menus和others的话应该只有admin角色
//                .antMatchers("/users", "/roles")
//                //.hasAuthority()   user可以访问users和roles admin可以访问users,roles,menus,others
//                .hasAnyAuthority("ROLE_user", "ROLE_admin")
//                .antMatchers("/menus", "/others")
//                .hasAnyRole("admin")
                .anyRequest()
                .authenticated()
                .and()
                //允许注销
                .logout().permitAll()
                .addLogoutHandler(myLogoutHandler)
                .logoutSuccessHandler(myLogoutSuccessHandler)
                //注销时删除cookie
                .deleteCookies("JSESSIONID")
                .and()
                //设置登录页面
                .formLogin().loginPage("/login.html")
                //设置form表单的登录控制器，默认是login 设置表单的action
                .loginProcessingUrl("/login")
                //登录表单form中用户名输入框input的name名，不输入默认为username
                .usernameParameter("username")
                //登录表单form中密码输入框input的name名，不输入默认为password
                .passwordParameter("password")
                //登录认证成功后默认的跳转路径
                //.defaultSuccessUrl("/home").permitAll()
                .successHandler(myAuthenticationSuccessHandler)
                // 登录失败Url
                //.failureUrl("/login/error")
                .failureHandler(myAuthenticationFailureHandler)
                .and()
                .exceptionHandling()
                .accessDeniedHandler(myAccessDeniedHandler)
                //未登录访问资源的时候提示异常
                .authenticationEntryPoint(myAuthenticationEntryPoint)
                .and()
                .sessionManagement().invalidSessionStrategy(myInvalidSessionStrategy)
                //最大允许的登录数量
                .maximumSessions(1)
                //是否允许另一个是账户登录
                .maxSessionsPreventsLogin(false)
                //被挤下线的处理方式
                .expiredSessionStrategy(myExpiredSessionStrategy);//sessionCreationPolicy(SessionCreationPolicy.STATELESS);//前后端分离建议使用stateless


        http.csrf().disable();//关闭跨域攻击
    }
}
